package com.krtech.wecard.module.pub.service.impl;

import cn.dev33.satoken.SaManager;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.krtech.wecard.framework.exception.BusinessException;
import com.krtech.wecard.framework.utils.LoginUtil;
import com.krtech.wecard.module.ext.entity.ExtLog;
import com.krtech.wecard.module.ext.mapper.ExtLogMapper;
import com.krtech.wecard.module.pub.dto.*;
import com.krtech.wecard.module.pub.entity.TnNotifyModel;
import com.krtech.wecard.module.pub.entity.TnNotifyReback;
import com.krtech.wecard.module.pub.entity.TnNotifyUser;
import com.krtech.wecard.module.pub.service.TnNotifyModelService;
import com.krtech.wecard.module.pub.service.TnNotifyUserService;
import com.krtech.wecard.module.sys.entity.*;
import com.krtech.wecard.module.sys.enums.SysEnums;
import com.krtech.wecard.module.sys.mapper.SysUserRoleMapper;
import com.krtech.wecard.module.sys.service.LoginService;
import com.krtech.wecard.module.sys.service.SysKeyValueService;
import com.krtech.wecard.module.sys.service.SysUserService;
import com.krtech.wecard.module.sys.service.impl.LoginServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringEscapeUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.lang.reflect.Method;
import java.util.*;

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import java.util.List;
import java.util.stream.Collectors;

import com.krtech.wecard.module.pub.entity.TnNotify;
import com.krtech.wecard.module.pub.mapper.TnNotifyMapper;
import com.krtech.wecard.module.pub.service.TnNotifyService;
import org.springframework.transaction.annotation.Transactional;

@Service
@Slf4j
public class TnNotifyServiceImpl extends ServiceImpl<TnNotifyMapper, TnNotify> implements TnNotifyService {

    @Override
    public int updateBatch(List<TnNotify> list) {
        return baseMapper.updateBatch(list);
    }

    @Override
    public int batchInsert(List<TnNotify> list) {
        return baseMapper.batchInsert(list);
    }


    @Autowired
    private TnNotifyMapper notifyMapper;

    @Autowired
    private TnNotifyUserService notifyUserService;

    @Autowired
    private SysUserService sysUserService;

    @Autowired
    private SysUserRoleMapper userRoleMapper;

    @Autowired
    private SysKeyValueService keyValueService;

    @Autowired
    private LoginServiceImpl loginService;

    @Autowired
    private ExtLogMapper extLogMapper;

    @Autowired
    private TnNotifyModelService notifyModelService;

    @Autowired
    private TnNotifyRebackServiceImpl notifyRebackService;


    /**
     * 查询某个模块，某个人的消息列表
     *
     * @param dto
     * @param page
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public IPage<MyTnNotify> search(NotifySearchDto dto, Page<TnNotify> page) {
        IPage<MyTnNotify> myNotify = notifyMapper.getMyNotifyPage(dto, page);
        return myNotify;
    }

    @Override
    public Integer getNoReadInfo(NotifySearchDto dto) {
        return notifyMapper.getNoReadInfo(dto);
    }

    /**
     * 新增通知
     *
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public TnNotify add(TnNotify notify) {
        SysUser userInfo = LoginUtil.getUserInfo();
//        notifyPushDto.setNotifyContent(StringEscapeUtils.unescapeHtml4(notifyPushDto.getNotifyContent()));
        //保存消息
//        TnNotify notify = new TnNotify();
//        BeanUtil.copyProperties(notifyPushDto,notify);

        notify.setNotifyCreatedTime(new Date());

        notify.setNotifySenderUserId(userInfo.getUserId());
        notify.setNotifySenderUserName(userInfo.getUserName());
        this.saveOrUpdate(notify);

        //存储要推送的用户
        HashSet<TnNotifyUser> notifyUserList = new HashSet<>();

        //根据不同的 推送范围
        //部门人员查找
        String deptListStr = notify.getDeptList();
        List<SysDept> deptList = null;
        if (StrUtil.isNotEmpty(deptListStr)) {
            deptList = JSONUtil.parseArray(deptListStr).toList(SysDept.class);
        }
        if (deptList != null && deptList.size() > 0) {
            for (SysDept dept : deptList) {
                List<SysUser> userByDeptAndSon = sysUserService.getUserByDeptAndSon(dept.getDeptId());

                for (SysUser user : userByDeptAndSon) {
                    TnNotifyUser tnNotifyUser = new TnNotifyUser();
                    tnNotifyUser.setNotifyId(notify.getNotifyId());
                    tnNotifyUser.setUserId(user.getUserId());
                    tnNotifyUser.setNotifyUserAlreadyRead(false);
                    notifyUserList.add(tnNotifyUser);
                }

            }
        }
        //根据角色id查询
        String roleListStr = notify.getRoleList();
        List<SysRole> roleList = null;
        if (StrUtil.isNotEmpty(roleListStr)) {
            roleList = JSONUtil.parseArray(roleListStr).toList(SysRole.class);
        }


        if (roleList != null && roleList.size() > 0) {
            for (SysRole role : roleList) {
                List<SysUserRole> userRoleList = userRoleMapper.selectList(new QueryWrapper<SysUserRole>().eq("role_id", role.getRoleId()));

                for (SysUserRole userRole : userRoleList) {
                    TnNotifyUser tnNotifyUser = new TnNotifyUser();
                    tnNotifyUser.setNotifyId(notify.getNotifyId());
                    tnNotifyUser.setUserId(userRole.getUserId());
                    tnNotifyUser.setNotifyUserAlreadyRead(false);
                    notifyUserList.add(tnNotifyUser);
                }

            }
        }
        //根据用户id查询
        //查询userList在当前环境的userId
        List<SysUser> getUserIdList = null;

        String userListStr = notify.getUserList();
        List<SysUser> userList = null;
        if (StrUtil.isNotEmpty(userListStr)) {
            userList = JSONUtil.parseArray(userListStr).toList(SysUser.class);
            List<String> userCodeList = userList.stream()
                    .filter(user -> StrUtil.isNotEmpty(user.getUserCode()))
                    .map(user -> user.getUserCode()).collect(Collectors.toList());

            getUserIdList = sysUserService.list(new QueryWrapper<SysUser>().in(SysUser.COL_USER_CODE, userCodeList).eq(SysUser.COL_DEL_FLAG, 0));
        }

        if (userList != null && userList.size() > 0) {
            for (SysUser user : userList) {
                TnNotifyUser tnNotifyUser = new TnNotifyUser();
                tnNotifyUser.setNotifyId(notify.getNotifyId());
                if (user.getUserId() != null) {
                    tnNotifyUser.setUserId(user.getUserId());
                }

                if (StrUtil.isNotEmpty(user.getUserCode())) {
                    tnNotifyUser.setUserCode(user.getUserCode());
                    // 根据userCode，将userId也添上
                    List<SysUser> nowUser = getUserIdList.stream().filter(userId -> userId.getUserCode().equals(user.getUserCode())).collect(Collectors.toList());
                    if (nowUser.size() > 0) {
                        tnNotifyUser.setUserId(nowUser.get(0).getUserId());
                    }
                }

                tnNotifyUser.setNotifyUserAlreadyRead(false);
                notifyUserList.add(tnNotifyUser);
            }

        }


        List<TnNotifyUser> collect = notifyUserList.stream().collect(Collectors.toList());
        if (collect.size() > 0) {
            notifyUserService.batchInsert(collect);
        }

        // 是否发送到微信公众号
        SysKeyValue hasNotifyPermisson = keyValueService.getOne(new QueryWrapper<SysKeyValue>().eq(SysKeyValue.COL_SYS_NAME, SysEnums.HAS_NOTOFY_PERMISSIONS));
        if (hasNotifyPermisson != null && hasNotifyPermisson.getSysValue().equals(SysEnums.HAS_NOTOFY_PERMISSIONS_ON)) {

            //发送消息
            if (collect.size() > 0) {
                List<Long> userIdList = collect.stream().map(TnNotifyUser::getUserId).collect(Collectors.toList());
                List<SysUser> sendUserList = sysUserService.list(new QueryWrapper<SysUser>().in(SysUser.COL_USER_ID, userIdList));

                SysKeyValue moduleId = keyValueService.getOne(new QueryWrapper<SysKeyValue>()
                        .eq(SysKeyValue.COL_SYS_NAME, SysEnums.PUBLIC_NOTIFY_MODULE_ID));

                for (SysUser sendUser : sendUserList) {
                    if (StrUtil.isNotEmpty(sendUser.getPublicOpenId())) {
                        //发送消息
                        ResultState templateMsgResultDto = noticeTemplateByCode(notify, sendUser.getPublicOpenId(), notify.getNotifyTargetType());
                        log.info("发送消息结果" + templateMsgResultDto.toString());

                        // 失败重发机制，加入redis，让定时器重发
                        if (!"ok".equals(templateMsgResultDto.getErrmsg())) {
                            if (templateMsgResultDto.getErrcode() == 40001) {
                                //access_toke过期
                                loginService.deletePublicAccessToken();
                            } else if (templateMsgResultDto.getErrcode() == 43004) {
                                //用户未关注
                                SysUser user = sysUserService.getOne(new QueryWrapper<SysUser>().eq(SysUser.COL_PUBLIC_OPEN_ID, sendUser.getPublicOpenId()));
                                if (user != null) {
                                    user.setHasFocusPublicNumber(0);
                                    sysUserService.updateById(user);
                                }

                            }
                            //todo 重发

                            TnNotifyReback notifyReback = new TnNotifyReback();
                            notifyReback.setCreatedTime(new Date());
                            notifyReback.setJsonBlock(JSONUtil.toJsonStr(notify));
                            notifyReback.setStatus(0);
                            notifyReback.setSendOpenId(sendUser.getPublicOpenId());
                            notifyRebackService.save(notifyReback);

                        }
                    }
                }

            }

        }

        //todo 是否发送短信

        return notify;
    }

    public ResultState noticeTemplateByCode(TnNotify notify, String publicOpenId, String code) {
        SysKeyValue appId = keyValueService.getOne(new QueryWrapper<SysKeyValue>()
                .eq(SysKeyValue.COL_SYS_NAME, SysEnums.WX_PUBLIC_NUMBER_APPID));
        SysKeyValue secret = keyValueService.getOne(new QueryWrapper<SysKeyValue>()
                .eq(SysKeyValue.COL_SYS_NAME, SysEnums.WX_PUBLIC_NUMBER_SECRET));

        SysKeyValue schoolName = keyValueService.getOne(new QueryWrapper<SysKeyValue>()
                .eq(SysKeyValue.COL_SYS_NAME, SysEnums.SCHOOLE_NAME));

        SysKeyValue targetUrl = keyValueService.getOne(new QueryWrapper<SysKeyValue>()
                .eq(SysKeyValue.COL_SYS_NAME, SysEnums.WX_PUBLIC_NUMBER_TARGET_URL));


        // 模版ID
        TnNotifyModel notifyModel = notifyModelService.getOne(new QueryWrapper<TnNotifyModel>().eq(TnNotifyModel.COL_CODE, code));
        if (notifyModel == null) {
            throw new BusinessException("您的code没有");
        }


        TreeMap<String, TreeMap<String, String>> params = new TreeMap<>();
        //根据具体模板参数组装
        //标题
        if (StrUtil.isNotEmpty(notifyModel.getKeyword1())) {
            SysKeyValue readValue1 = readValue(notify, notifyModel, 1);
            params.put(readValue1.getSysKey(), WxTemplateMsg.item(readValue1.getSysValue(), "#000000"));
        }

        if (StrUtil.isNotEmpty(notifyModel.getKeyword2())) {
            SysKeyValue readValue2 = readValue(notify, notifyModel, 2);
            params.put(readValue2.getSysKey(), WxTemplateMsg.item(readValue2.getSysValue(), "#000000"));
        }

        if (StrUtil.isNotEmpty(notifyModel.getKeyword3())) {
            SysKeyValue readValue3 = readValue(notify, notifyModel, 3);
            params.put(readValue3.getSysKey(), WxTemplateMsg.item(readValue3.getSysValue(), "#000000"));
        }

        if (StrUtil.isNotEmpty(notifyModel.getKeyword4())) {
            SysKeyValue readValue4 = readValue(notify, notifyModel, 4);
            params.put(readValue4.getSysKey(), WxTemplateMsg.item(readValue4.getSysValue(), "#000000"));
        }

        if (StrUtil.isNotEmpty(notifyModel.getKeyword5())) {
            SysKeyValue readValue5 = readValue(notify, notifyModel, 5);
            params.put(readValue5.getSysKey(), WxTemplateMsg.item(readValue5.getSysValue(), "#000000"));
        }


        WxTemplateMsg wxTemplateMsg = new WxTemplateMsg();
        // 模版ID
        wxTemplateMsg.setTemplate_id(notifyModel.getModelId());
        // openId
        wxTemplateMsg.setTouser(publicOpenId);
        // 关键字赋值
        wxTemplateMsg.setData(params);
        //跳转到我们写的url
        //是否发送ur
        if (StrUtil.isNotEmpty(notify.getAppUrl())) {
            wxTemplateMsg.setUrl(notify.getAppUrl());
        } else {
            if (notify.getHasOpenUrl() == null || notify.getHasOpenUrl() == 1) {
                wxTemplateMsg.setUrl(targetUrl.getSysValue() + "?id=" + notify.getNotifyId());
            }
        }

        String data = JSONUtil.toJsonStr(wxTemplateMsg);
        return handleSendMsgLog(data);
    }

    public SysKeyValue readValue(TnNotify notify, TnNotifyModel notifyModel, int index) {
        try {
            SysKeyValue keyValue = new SysKeyValue();


            //根据index获得keyword值
            Class<? extends TnNotifyModel> modelClass = notifyModel.getClass();
            Method modelGetKeywordMethod = modelClass.getMethod("getKeyword" + index);
            Object keywordKey = modelGetKeywordMethod.invoke(notifyModel);

            Method getValueMethod = modelClass.getMethod("getValue" + index);
            Object valueVal = getValueMethod.invoke(notifyModel);

            if (keywordKey == null || valueVal == null) {
//                throw new BusinessException("反射获得发送消息模版数据异常1");
                throw new BusinessException("获得模版keyword，您的参数:"+valueVal+",为空异常");
            }
            String keyword = keywordKey.toString();
            String value = valueVal.toString();

            if (StrUtil.isNotEmpty(keyword)) {

                keyValue.setSysKey(keyword);

                Class<? extends TnNotify> notifyClass = notify.getClass();

                String name = value.substring(0, 1).toUpperCase() + value.substring(1);
                Method method = notifyClass.getMethod("get" + name);
                Object dataValueObj = method.invoke(notify);
                if (dataValueObj == null){
                    throw new BusinessException("获得模版value，您的参数:"+name+",为空异常");
                }

                keyValue.setSysValue(dataValueObj.toString());
                return keyValue;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        throw new BusinessException("反射获得发送消息模版数据异常2");
    }

    /**
     * 正常发送消息
     *
     * @param notify
     * @param publicOpenId
     * @param templateId
     * @return
     */
    public ResultState noticeTemplate(TnNotify notify, String publicOpenId, String templateId) {
        SysKeyValue appId = keyValueService.getOne(new QueryWrapper<SysKeyValue>()
                .eq(SysKeyValue.COL_SYS_NAME, SysEnums.WX_PUBLIC_NUMBER_APPID));
        SysKeyValue secret = keyValueService.getOne(new QueryWrapper<SysKeyValue>()
                .eq(SysKeyValue.COL_SYS_NAME, SysEnums.WX_PUBLIC_NUMBER_SECRET));

        SysKeyValue schoolName = keyValueService.getOne(new QueryWrapper<SysKeyValue>()
                .eq(SysKeyValue.COL_SYS_NAME, SysEnums.SCHOOLE_NAME));

        SysKeyValue targetUrl = keyValueService.getOne(new QueryWrapper<SysKeyValue>()
                .eq(SysKeyValue.COL_SYS_NAME, SysEnums.WX_PUBLIC_NUMBER_TARGET_URL));


        // 模版ID

        TreeMap<String, TreeMap<String, String>> params = new TreeMap<>();
        //根据具体模板参数组装
        //标题
        params.put("first", WxTemplateMsg.item(notify.getNotifyTitle(), "#000000"));
        //学校名称
//        params.put("keyword1", WxTemplateMsg.item(schoolName.getSysValue(), "#000000"));
        params.put("keyword1", WxTemplateMsg.item("", "#000000"));
        //发送人名称
//        params.put("keyword2", WxTemplateMsg.item(notify.getNotifySenderUserName(), "#000000"));
        params.put("keyword2", WxTemplateMsg.item(notify.getNotifyTitle(), "#000000"));
        //发送时间
        params.put("keyword3", WxTemplateMsg.item(DateUtil.format(notify.getNotifyCreatedTime(), "yyyy-MM-dd HH:mm:ss"), "#000000"));
        //发送内容
//        params.put("keyword4", WxTemplateMsg.item(notify.getNotifyContent(), "#000000"));
        params.put("keyword4", WxTemplateMsg.item("", "#000000"));
        //备注
        params.put("remark", WxTemplateMsg.item(StringEscapeUtils.unescapeXml(notify.getNotifyContent()), "#000000"));
        WxTemplateMsg wxTemplateMsg = new WxTemplateMsg();
        // 模版ID
        wxTemplateMsg.setTemplate_id(templateId);
        // openId
        wxTemplateMsg.setTouser(publicOpenId);
        // 关键字赋值
        wxTemplateMsg.setData(params);
        //跳转到我们写的url
        wxTemplateMsg.setUrl(targetUrl.getSysValue() + "?id=" + notify.getNotifyId());

        String data = JSONUtil.toJsonStr(wxTemplateMsg);
        return handleSendMsgLog(data);
    }


    /**
     * 先关注再上小程序通知接口
     *
     * @param notify
     * @param publicOpenId
     * @param templateId
     * @return
     */
    public ResultState noticeTemplateFocusPre(TnNotify notify, String publicOpenId, String templateId) {
        SysKeyValue appId = keyValueService.getOne(new QueryWrapper<SysKeyValue>()
                .eq(SysKeyValue.COL_SYS_NAME, SysEnums.WX_PUBLIC_NUMBER_APPID));
        SysKeyValue secret = keyValueService.getOne(new QueryWrapper<SysKeyValue>()
                .eq(SysKeyValue.COL_SYS_NAME, SysEnums.WX_PUBLIC_NUMBER_SECRET));

        SysKeyValue schoolName = keyValueService.getOne(new QueryWrapper<SysKeyValue>()
                .eq(SysKeyValue.COL_SYS_NAME, SysEnums.SCHOOLE_NAME));

        SysKeyValue targetUrl = keyValueService.getOne(new QueryWrapper<SysKeyValue>()
                .eq(SysKeyValue.COL_SYS_NAME, SysEnums.WX_PUBLIC_NUMBER_TARGET_URL));


        // 模版ID

        TreeMap<String, TreeMap<String, String>> params = new TreeMap<>();
        //根据具体模板参数组装
        //标题
        params.put("first", WxTemplateMsg.item(notify.getNotifyTitle(), "#000000"));
        //账号名称
        params.put("keyword1", WxTemplateMsg.item(notify.getNotifySenderUserName(), "#000000"));
        //发送人名称
        params.put("keyword3", WxTemplateMsg.item("", "#000000"));
        //发送时间
        params.put("keyword2", WxTemplateMsg.item(DateUtil.format(notify.getNotifyCreatedTime(), "yyyy-MM-dd HH:mm:ss"), "#000000"));
        //发送内容
//        params.put("keyword4", WxTemplateMsg.item(notify.getNotifyContent(), "#000000"));
        params.put("keyword4", WxTemplateMsg.item("", "#000000"));
        //备注
        params.put("remark", WxTemplateMsg.item(StringEscapeUtils.unescapeXml(notify.getNotifyContent()), "#000000"));
        WxTemplateMsg wxTemplateMsg = new WxTemplateMsg();
        // 模版ID
        wxTemplateMsg.setTemplate_id(templateId);
        // openId
        wxTemplateMsg.setTouser(publicOpenId);
        // 关键字赋值
        wxTemplateMsg.setData(params);
        //跳转到我们写的url
        wxTemplateMsg.setUrl(targetUrl.getSysValue() + "?id=" + notify.getNotifyId());

        String data = JSONUtil.toJsonStr(wxTemplateMsg);
        return handleSendMsgLog(data);
    }


    private ResultState handleSendMsgLog(String data) {
        ResultState resultDto = new ResultState();
        try {
            resultDto = sendTemplateMsg(data);
        } catch (Exception exception) {
            log.error("发送模版失败", exception);
        }
        //  可以记录一下发送记录的日志
        if (resultDto.getErrcode() != 43004) {
            //用户未关注，不记录
            ExtLog extLog = new ExtLog();
            extLog.setUri("发布消息到微信公众号");
            extLog.setRequest(data);
            extLog.setResponse(resultDto.toString());
            extLogMapper.insert(extLog);
        }

        return resultDto;
    }

    public ResultState sendTemplateMsg(String data) throws Exception {
        // 获取token
        String accessToken = loginService.getPublicAccessToken();
        // 发送消息
        String post = HttpUtil.post("https://api.weixin.qq.com/cgi-bin/message/template/send?access_token=" + accessToken, data);
        log.info("消息发送返回:" + post);
        return JSONUtil.toBean(post, ResultState.class);
    }


    @Transactional(rollbackFor = Exception.class)
    @Override
    public Boolean userRead(Long notifyUserId) {
        TnNotifyUser notifyUser = notifyUserService.getById(notifyUserId);
        if (notifyUser != null) {
            notifyUser.setNotifyUserAlreadyRead(true);
            notifyUserService.updateById(notifyUser);

            //更新已读数量
            TnNotify notify = this.getById(notifyUser.getNotifyId());
            notify.setReadCount(notify.getReadCount() + 1);
            this.updateById(notify);

        } else {
            throw new BusinessException(notifyUserId + ",该消息不存在");
        }
        return true;
    }

    /**
     * 管理员删除消息
     *
     * @param notifyId
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public Boolean adminDelete(Long notifyId) {
        notifyUserService.remove(new QueryWrapper<TnNotifyUser>()
                .eq(TnNotifyUser.COL_NOTIFY_ID, notifyId));

        this.remove(new QueryWrapper<TnNotify>()
                .eq(TnNotify.COL_NOTIFY_ID, notifyId));
        return true;
    }
}










